// 
// OV-chip 2.0 project
// 
// Digital Security (DS) group at Radboud Universiteit Nijmegen
// 
// Copyright (C) 2008, 2009
// 
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of
// the License, or (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License in file COPYING in this or one of the
// parent directories for more details.
// 
// Created 4.12.08 by Hendrik
// 
// host test frame
// 
// $Id: Host_testframe.java,v 1.17 2010-03-12 15:40:20 tews Exp $

#include <config>

package ds.ov2.front;


import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;
import java.math.BigInteger;
import java.security.NoSuchAlgorithmException;
import javax.smartcardio.CardException;

import ds.ov2.util.Option;
import ds.ov2.util.Bool_option;
import ds.ov2.front.PTLS_rsa_parameters.PTLS_io_exception;


/** 
 * Main class for the host test frame that combines the OV-chip applet
 * and host driver code to be tested on a standard JVM. The host test
 * frame is great for debugging the applet code, because here the
 * applet runs on the PC and it can print any amount of debugging
 * output. After debugging the same code can run on a real Java Card.
 * With different preprocessor configurations this file provides the
 * main class for three host test frames: The host test frame for the
 * plain RSA applet, the host test frame for the Montgomerizing
 * applet, and the host test frame for the squaring applet. They are
 * started with the scripts {@code plainhosttestframe}, {@code
 * monthosttestframe}, and {@code squarehosttestframe}, respectively.
 * <P>
 *
 * In the host test frame the test stub code is substituted for the
 * real stub code. Both are generated by the IDL compiler and have the
 * same public interface. The real stub code calls {@link
 * ds.ov2.util.Host_protocol#run_step Host_protocol.run_step} to
 * communicate to a Java Card or an emulator. Instead the test stub
 * code calls the applet code directly, causing the applet and the
 * host driver code to run together on the same JVM. <P>
 *
 * The three host test frames are very similar to the card test frame
 * {@link Card_testframe}. Here we describe only the differences and
 * give a complete list of the options.
 *
 * <DL>
 * <DT><STRONG>Test save/restore PTLS parameters</STRONG>
 * <DD>This test frame provides an additional feature: Test the
 * save/restore functionality for PTLS parameters. This test generates
 * {@link Test_state#ptls_param_rounds} many PTLS parameter sets,
 * writes them to disc and reads them back to see if there are any
 * differences. 
 *
 * <DT><STRONG>Applet choice</STRONG>
 * <DD>Whether to test the plain, the Montgomerizing, or the squaring
 * RSA applet cannot be selected via options. Instead, there are three
 * different applications, {@code plainhosttestframe} for the Plain
 * RSA applet, {@code monthosttestframe} for the Montgomerizing RSA
 * applet, and {@code squarehosttestframe} for the squaring RSA applet.
 *
 * <DT><STRONG>Unsupported Options</STRONG>
 * <DD>The following options are not supported:
 * {@code -jcop, -sun, -c, -r, -list-readers}
 *
 * </DL>
 *
 * The three host test frames accept the following options.
 *
 * <DL>
 * <DT><STRONG>-test-size</STRONG>
 * <DD>Test loop with increasing key size
 *
 * <DT><STRONG>-test-const</STRONG>
 * <DD>Test a fixed key size.
 *
 * <DT><STRONG>-test-ptls-save</STRONG>
 * <DD>Test PTLS parameter save/restore. The number of rounds is
 * determined by the number of ptls key generation rounds (see option
 * {@code -ptls-rounds}).
 *
 * <DT><STRONG>-base-length n</STRONG>
 * <DD>Set key length to n bits, which will be identical to the base
 * size. If the exponent length has not been set before automatically
 * adapts the exponent length according to Lenstra's "Key length"
 * estimations. 
 *
 * <DT><STRONG>-exp-length n</STRONG>
 * <DD>Set the exponent length to n bits.
 *
 * <DT><STRONG>-attr n</STRONG>
 * <DD>Set number of attributes that the card receives during
 * personalization. 
 *
 * <DT><STRONG>-ptls-rounds n</STRONG>
 * <DD>Number of ptls key generation rounds.
 *
 * <DT><STRONG>-card-init-rounds n</STRONG>
 * <DD>Number of card init generation rounds.
 *
 * <DT><STRONG>-resign-rounds n</STRONG>
 * <DD>Number of resigning rounds.
 *
 * <DT><STRONG>-proof-rounds n</STRONG>
 * <DD>Number of proof rounds.
 *
 * <DT><STRONG>-768</STRONG>
 * <DD>Use the RSA 768 factorization of 2009 to calibrate the security
 * parameter estimation
 *
 * <DT><STRONG>-ignore</STRONG>
 * <DD>Ignore all wrong certificates and proofs.
 *
 * <DT><STRONG>-i n</STRONG>
 * <DD>Provide decimal n as input for the test.
 *
 * <DT><STRONG>-hex n</STRONG>
 * <DD>Provide hexadecimal n as input for the test.
 *
 * <DT><STRONG>-h</STRONG>
 * <DD>Print usage information.
 *
 * <DT><STRONG>-d, -dd, -ddd</STRONG>
 * <DD>Print debug and progress messages, the more d's the more
 * messages.
 * </DL>
 * 
 *
 * This class contains the special parts for the host test frame,
 * especially the test for saving PTLS parameters. Many parts that are
 * shared with the card test frame ({@link Card_testframe}) are
 * contained in {@link Testframe}.
 * <P>
 *
 * Static class.
 * 
 *
 * @author Hendrik Tews
 * @version $Revision: 1.17 $
 * @commitdate $Date: 2010-03-12 15:40:20 $ by $Author: tews $
 * @environment host
 * @CPP This class uses the following cpp defines:
 *   <a href="../../../overview-summary.html#RSA_CARD">RSA_CARD<a>
 */
public class Host_testframe {

    /**
     * 
     * Static class, object creation disabled.
     */
    protected Host_testframe() {}

    /** Short application name for error messages. */
    public static final String short_application_name = 
        "Test_host (" + Test_state.applet_type.ref + ")";

    /** Long appliation name. */
    public static final String long_application_name = 
        "Host test application (" + Test_state.applet_type.ref + ")";


    //########################################################################
    // configuration section, argument parsing
    //

    /**
     * 
     * Array with (only one) host test frame specific option.
     */
    public static final Option[] host_test_frame_options = new Option[]{
        new Bool_option("-test-ptls-save", Test_state.test_ptls_save,
                  "test PTLS parameter save/restore instead of protocols")
        };


    //########################################################################
    // test code
    //



    /**
     * 
     * Simulate the applet life with several sets of PTLS parameters
     * for the configures base and exponent sizes. The numbers of what
     * is simulated how often is determined by some global variables
     * that can be changed via the command line, see {@link
     * Card_testframe}.
     * <P>
     *
     * Top level action, called when {@code -test-const} was
     * specified.
     * 
     * @throws CardException on communication errors
     * @throws IOException if the applet cap file is not accessible
     * @throws NoSuchAlgorithmException if no provider for RSA key
     * generation can be found
     */
    public static void test_const_size() 
        throws NoSuchAlgorithmException, CardException, IOException
    {
        PrintWriter out = new PrintWriter(System.out, true);
        if(Test_state.verbosity.ref >= 15) {
            RSA_data.out = out;
            ds.ov2.bignat.Vector.out = out;
            RSA_CARD.out = out;
            Host_signature.out = out;
        }

        Testframe.test_init_resign_proof(null, out,
                                         State.attribute_number.ref,
                                         State.base_length.ref,
                                         State.exponent_length.ref);
    }


    /**
     * 
     * Simulate the applet life with several sets of PTLS parameters
     * for increasing base and exponent sizes. The numbers of what
     * is simulated how often is determined by some global variables
     * that can be changed via the command line, see {@link
     * Card_testframe}.
     * <P>
     *
     * Top level action, called when {@code -test-size} was
     * specified.
     * 
     * @throws CardException on communication errors
     * @throws IOException if the applet cap file is not accessible
     * @throws NoSuchAlgorithmException if no provider for RSA key
     * generation can be found
     */
    public static void test_increase_size() 
        throws NoSuchAlgorithmException, CardException, IOException
    {
        PrintWriter out = new PrintWriter(System.out, true);
        if(Test_state.verbosity.ref >= 15) {
            RSA_data.out = out;
            ds.ov2.bignat.Vector.out = out;
            RSA_CARD.out = out;
            Host_signature.out = out;
        }

        Testframe.test_size_loop(null, out,
                                 State.attribute_number.ref,
                                 State.base_length.ref);
    }

        

    /**
     * 
     * Character stream that collects the contents in a buffer in
     * memory that can be retrieved later.
     */
    static class Buffered_string_writer extends java.io.Writer {

        /** Contents of the Buffered_string_writer. */
        private StringBuilder contents = new StringBuilder();

        /** Empty constructor. */
        public Buffered_string_writer() {}

        /** Empty close. */
        public void close() {}

        /** Empty flush. */
        public void flush() {}

        /**
         * 
         * Append a portion of an array to the memory buffer.
         * 
         * @param cbuf array of characters
         * @param off starting offset
         * @param len length
         */
        public void write(char[] cbuf, int off, int len) {
            contents.append(cbuf, off, len);
        }


        /**
         * 
         * Returns the current contents of the stream.
         */
        public String get_contents() {
            return contents.toString();
        }
    }


    /**
     * 
     * Test writing/reading PTLS parameters to/from file. Performs
     * {@link Test_state#ptls_param_rounds} many tests to save a PTLS
     * parameter set to the disk and read it back.
     * <P>
     *
     * Top level action, called when {@code -test-ptls-save} was
     * given.
     * 
     * @throws NoSuchAlgorithmException if no provider for RSA key
     * generation can be found
     * @throws FileNotFoundException if the file with the saved
     * parameters disappeared
     */
    public static void test_ptls_save()
        throws NoSuchAlgorithmException, 
               FileNotFoundException,
               PTLS_io_exception
    {
        PrintWriter out = new PrintWriter(System.out, true);

        for(int i = 0; i < Test_state.ptls_param_rounds.ref; i++) {
            out.format("Round %d ", i);
            out.flush();
            PTLS_rsa_parameters orig = 
                new PTLS_rsa_parameters(State.attribute_number.ref, 
                                        out, Test_state.verbosity.ref);
            orig.generate(State.base_length.ref, 
                          State.exponent_length.ref,
                          Test_state.applet_type.ref);
            out.print("original generated ");
            out.flush();
            
            Buffered_string_writer orig_sw = new Buffered_string_writer();
            orig.print_all(Test_state.applet_type.ref, 
                           new PrintWriter(orig_sw));
            String os = orig_sw.get_contents();

            orig.write_to_file("save.ptls");
            out.println("and written");
            out.flush();

            PTLS_rsa_parameters copy = 
                PTLS_rsa_parameters.read_from_file("save.ptls", 
                                                   Test_state.applet_type.ref,
                                                   out, 
                                                   Test_state.verbosity.ref);
            out.print("copy read ");
            out.flush();

            Buffered_string_writer copy_sw = new Buffered_string_writer();
            copy.print_all(Test_state.applet_type.ref, 
                           new PrintWriter(copy_sw));
            String cs = copy_sw.get_contents();

            if(os.equals(cs)) {
                out.print("OK\n\n");
                out.flush();
            }
            else {
                out.format("ERROR\noriginal:\n%s\ncopy:\n%s\n\n",
                           os, cs);
                System.exit(1);
            }
        }
    }


    /**
     * 
     * Main method of the host test frames. Parses the command line
     * and then invokes the specified tests.
     * 
     * @param args command line
     * @throws IOException if the applet cap file is not accessible or
     * a read/write error occurs
     * @throws NoSuchAlgorithmException if no provider for RSA key
     * generation can be found
     * @throws FileNotFoundException if the file with the saved
     * parameters disappeared
     * @throws CardException for communication errors with the card
     * @throws PTLS_io_exception for errors when writing/reading PTLS
     * parameters 
     */
    public static void main(String[] args) 
        throws NoSuchAlgorithmException, CardException,
               FileNotFoundException, PTLS_io_exception, IOException
    {
        System.out.println(long_application_name);

        Test_state.sleep_time = 0;

        Testframe.parse_commandline(args, host_test_frame_options, 
                                    short_application_name);

        if(Test_state.test_increase_size.ref)
            test_increase_size();
        else if(Test_state.test_const_size.ref)
            test_const_size();
        else if(Test_state.test_ptls_save.ref) 
            test_ptls_save();
    }

}
